## -*- coding: utf-8 -*-
'''
Created on 12/giu/2009

@author: sax
'''

from django.core.urlresolvers import reverse, resolve, NoReverseMatch
from django.utils.functional import update_wrapper
from django.db import models
from django.utils.translation import ugettext as _
from django.contrib import  admin
from django.http import HttpResponse, HttpResponseRedirect
from xadmin.ajax.service import AjaxService
from xadmin.ajax import DatePickerWidget
from xadmin.widgets import *
from django.utils.encoding import force_unicode
import logging
import filterspecs #@UnusedImport
from xadmin.ajax.widgets.autocomplete import AjaxLookupInput
from xadmin.xbuttons import XModelAdminButton, XActionButton
from django.core.exceptions import PermissionDenied
from xadmin.util import unquote
from copy import copy
from django import forms, template
from django.forms.formsets import all_valid
from django.forms.models import modelform_factory, modelformset_factory, inlineformset_factory
from django.forms.models import BaseInlineFormSet
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin import widgets
from django.contrib.admin import helpers
from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict
from django.core.exceptions import PermissionDenied
from django.db import models, transaction
from django.db.models.fields import BLANK_CHOICE_DASH
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render_to_response
from django.utils.datastructures import SortedDict
from django.utils.functional import update_wrapper
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.functional import curry
from django.utils.text import capfirst, get_text_list
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext, ugettext_lazy
from django.utils.encoding import force_unicode
LOGGER = logging.getLogger('xadmin.xamodeladmin')


from xadmin.options import IncorrectLookupParameters
from django.template.context import Context
from django.template.loader import get_template
from django.utils.safestring import mark_safe
from django import forms
from django.contrib.admin import widgets

class ROField(models.Field):
    pass

XFORMFIELD_FOR_DBFIELD_DEFAULTS = {
                                   models.URLField:    {'widget': URLWidget},
                                   models.DateField:   {'widget': DatePickerWidget},
                                   ROField:    {'widget': ReadOnlyWidget},
                                   }
FORMFIELD_FOR_DBFIELD_DEFAULTS = {
    models.DateTimeField: {
        'form_class': forms.SplitDateTimeField,
        'widget': widgets.AdminSplitDateTime
    },
    models.DateField:    {'widget': DatePickerWidget},
    models.TimeField:    {'widget': widgets.AdminTimeWidget},
    models.TextField:    {'widget': widgets.AdminTextareaWidget},
    models.URLField:     {'widget': URLWidget},
    models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
    models.CharField:    {'widget': widgets.AdminTextInputWidget},
    models.ImageField:   {'widget': widgets.AdminFileWidget},
    models.FileField:    {'widget': widgets.AdminFileWidget},
}


class ExtraButtonProvider(object):
    buttons = []
    def _get_extra_buttons(self, request, object_id=None):
        LOGGER.debug("")
        btns = []
        for id, cmd in enumerate(self.buttons):
            func, action, label = self.get_action(cmd)
            btns.append( XActionButton(id, object_id, label) )
        return btns    
    
    def op_view(self, request, object_id, action_index, extra_context=None):
        func = self.list_commands[int(action_index)]
        func, action, description = self.get_action( func )
        import inspect
        args, varargs, varkw, defaults = inspect.getargspec(func)
        params = {'request' : request,
                  'queryset': self.model._default_manager.get_query_set().filter(pk=object_id),
                  'object_id': object_id,
                  'modeladmin': self
                  } 

        if 'self' in args:
            return func(self, **params)
        else:
            return func(**params)
        
class AjaxProvider(object):
    _services = {}
    def setup_service_for_foreign_key(self):
        LOGGER.debug(self.model)
        self.fk_service_viewname = '%s_%s_service_fk' % (self.model._meta.app_label, self.model._meta.module_name)
        if isinstance(self.fk_service, AjaxService):
            self._services['fk'] = (self.fk_service_viewname, self.fk_service)

        elif isinstance(self.fk_service, (list, tuple)) and isinstance(self.fk_service[0], (list, tuple)):
            self.fk_service[0].insert(0, 'pk')
            self._services['fk'] = (self.fk_service_viewname,
                                     AjaxService(self.model.objects.all(), self.fk_service[0], self.fk_service[1])
                                     )

        elif bool(self.fk_service):
            ret_fields = ['pk']
            criteria = []
            for f in self.fk_service:
                ret_fields.append(f)
                criteria.append('%s__istartswith' % f)
            self._services['fk'] = (self.fk_service_viewname,
                                     AjaxService(self.model.objects.all(), ret_fields, criteria)
                                     )

class XModelAdmin(admin.ModelAdmin, AjaxProvider, ExtraButtonProvider):
    disabled = []
    read_only_fields = []
    model_link_fields = []
    read_only = False
    read_only_inlines = []
    #buttons = []
    list_commands = []
    services = []
    fk_service = None
    search_help_text = None
    filters_mapping = {}

    verbose_name_plural = None

    _MODE_CHANGE = 1
    _MODE_ADD = 2
    _MODE_LIST = 3
    _MODE_UNKNOW = 4
    ##############
    
    # =XAdmin= Updated
    def formfield_for_dbfield(self, db_field, **kwargs):
        """
        Hook for specifying the form Field instance for a given database Field
        instance.

        If kwargs are given, they're passed to the form Field's constructor.
        """
        request = kwargs.pop("request", None)

        # If the field specifies choices, we don't need to look for special
        # admin widgets - we just need to use a select widget of some kind.
        if db_field.choices:
            return self.formfield_for_choice_field(db_field, request, **kwargs)

        # ForeignKey or ManyToManyFields
        if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)):
            # Combine the field kwargs with any options for formfield_overrides.
            # Make sure the passed in **kwargs override anything in
            # formfield_overrides because **kwargs is more specific, and should
            # always win.
            if db_field.__class__ in self.formfield_overrides:
                kwargs = dict(self.formfield_overrides[db_field.__class__], **kwargs)

            # Get the correct formfield.
            if isinstance(db_field, models.ForeignKey):
                formfield = self.formfield_for_foreignkey(db_field, request, **kwargs)
            elif isinstance(db_field, models.ManyToManyField):
                formfield = self.formfield_for_manytomany(db_field, request, **kwargs)

            # For non-raw_id fields, wrap the widget with a wrapper that adds
            # extra HTML -- the "add other" interface -- to the end of the
            # rendered output. formfield can be None if it came from a
            # OneToOneField with parent_link=True or a M2M intermediary.
            if formfield and db_field.name not in self.raw_id_fields:
                formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
            if db_field.name in self.read_only_fields:
                formfield.widget.__class__ = ForeignKeyReadOnlyWidget
                
            return formfield
        
        # =X= read only fields handling 
        if db_field.name in self.read_only_fields:
            kwargs['widget'] = LabelWidget
        else:
            # If we've got overrides for the formfield defined, use 'em. **kwargs
            # passed to formfield_for_dbfield override the defaults.
            for klass in db_field.__class__.mro():
                if klass in self.formfield_overrides:
                    kwargs = dict(self.formfield_overrides[klass], **kwargs)
                    return db_field.formfield(**kwargs)

        # For any other type of field, just call its formfield() method.
        return db_field.formfield(**kwargs)

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        """
        Get a form Field for a ForeignKey.
        """
        if db_field.name in self.raw_id_fields:
            kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel)
        elif db_field.name in self.radio_fields:
            kwargs['widget'] = widgets.AdminRadioSelect(attrs={
                'class': get_ul_class(self.radio_fields[db_field.name]),
            })
            kwargs['empty_label'] = db_field.blank and _('None') or None

        return db_field.formfield(**kwargs)
        
    #############
    def __init__(self, model, admin_site):
        self.model = model
        self.opts = model._meta
        self.admin_site = admin_site
        self.inline_instances = []
        for inline_class in self.inlines:
            inline_instance = inline_class(self.model, self.admin_site)
            self.inline_instances.append(inline_instance)
        if 'action_checkbox' not in self.list_display and self.actions is not None:
            self.list_display = ['action_checkbox'] +  list(self.list_display)
        if not self.list_display_links:
            for name in self.list_display:
                if name != 'action_checkbox':
                    self.list_display_links = [name]
                    break
        self.formfield_overrides.update(FORMFIELD_FOR_DBFIELD_DEFAULTS)
        super(XModelAdmin, self).__init__(model, admin_site)

    def get_change_form_template(self):
        opts = self.model._meta
        app_label = opts.app_label

        return self.change_form_template or [
            "xadmin/change_form.html",
            "2admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
            "2admin/%s/change_form.html" % app_label,
            "2admin/change_form.html"
        ]
        
    def _get_view_name(self, action):
        info = self.model._meta.app_label, self.model._meta.module_name
        return "%s_%s" % ('%s_%s' % info, action)

    def get_listview_name(self):
        return self._get_view_name('changelist')

    def get_addview_name(self):
        return self._get_view_name('add')

    def get_historyview_name(self):
        return self._get_view_name('history')

    def get_deleteview_name(self):
        return self._get_view_name('delete')

    def get_readview_name(self):
        return self._get_view_name('read')

    def get_changeview_name(self):
        return self._get_view_name('change')

    def get_opview_name(self):
        return self._get_view_name('op')

    def get_buttonview_name(self):
        return self._get_view_name('button')

    def _wrap_view(self, view):
        def wrapper(*args, **kwargs):
            return self.admin_site.admin_view(view)(*args, **kwargs)
        return update_wrapper(wrapper, view)
    
    def get_services_urls(self):
        LOGGER.debug(self.model)
        from django.conf.urls.defaults import patterns, url
        ret = []
        for fieldname, service in self._services.items():
            LOGGER.info("created service %s for %s" % (fieldname, service[0]))
            ret.append(url(r'^service/%s/$' % fieldname,
                                self._wrap_view(service[1]),
                                name=service[0]
                                ),
                        )

        return patterns('', *ret)
    
    # =XAdmin= Updated
    def get_urls(self):
        LOGGER.debug(self.model)
        from django.conf.urls.defaults import patterns, url
        from xadmin.utils import has_all_attrs
        info = self.model._meta.app_label, self.model._meta.module_name

        ret = self.get_services_urls()

        ret += patterns('',
            url(r'^$',
                self._wrap_view(self.changelist_view),
                name=self.get_listview_name()),
            url(r'^add/$',
                self._wrap_view(self.add_view),
                name=self.get_addview_name()),
            url(r'^(.+)/history/$',
                self._wrap_view(self.history_view),
                name=self.get_historyview_name()),
            url(r'^(.+)/delete/$',
                self._wrap_view(self.delete_view),
                name=self.get_deleteview_name()),
            url(r'^(.+)/read/$',
                self._wrap_view(self.read_view),
                name=self.get_readview_name()),
            url(r'^(.+)/op/(.+)$',
                self._wrap_view(self.op_view),
                name=self.get_opview_name()),
            url(r'^(.+)/$',
                self._wrap_view(self.change_view),
                name=self.get_changeview_name()),
        )
        return ret        

    def read_view(self, request, object_id, extra_context=None):
        obj = self._get_object_by_pk(object_id)

        if not self.has_read_permission(request, obj):
            raise PermissionDenied

        self.read_only = True
        return self.change_view(request, object_id, extra_context)
    
    # =XAdmin= Updated
    def change_view(self, request, object_id, extra_context=None):
        "The 'change' admin view for this model."
        model = self.model
        opts = model._meta

        try:
            obj = self.queryset(request).get(pk=unquote(object_id))
        except model.DoesNotExist:
            # Don't raise Http404 just yet, because we haven't checked
            # permissions yet. We don't want an unauthenticated user to be able
            # to determine whether a given object exists.
            obj = None

        if not self.has_change_permission(request, obj):
            raise PermissionDenied

        if obj is None:
            raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)})

        if request.method == 'POST' and request.POST.has_key("_saveasnew"):
            return self.add_view(request, form_url='../add/')

        ModelForm = self.get_form(request, obj)
        formsets = []
        if request.method == 'POST':
            form = ModelForm(request.POST, request.FILES, instance=obj)
            # =X= remove read only fields 
            for f in self.read_only_fields:
                del form.fields[f]
                
            if form.is_valid():
                form_validated = True
                new_object = self.save_form(request, form, change=True)
            else:
                form_validated = False
                new_object = obj
            prefixes = {}
            for FormSet in self.get_formsets(request, new_object):
                prefix = FormSet.get_default_prefix()
                prefixes[prefix] = prefixes.get(prefix, 0) + 1
                if prefixes[prefix] != 1:
                    prefix = "%s-%s" % (prefix, prefixes[prefix])
                formset = FormSet(request.POST, request.FILES,
                                  instance=new_object, prefix=prefix)
                formsets.append(formset)

            if all_valid(formsets) and form_validated:
                self.save_model(request, new_object, form, change=True)
                form.save_m2m()
                for formset in formsets:
                    self.save_formset(request, form, formset, change=True)

                change_message = self.construct_change_message(request, form, formsets)
                self.log_change(request, new_object, change_message)
                return self.response_change(request, new_object)

        else:
            form = ModelForm(instance=obj)
            prefixes = {}
            for FormSet in self.get_formsets(request, obj):
                prefix = FormSet.get_default_prefix()
                prefixes[prefix] = prefixes.get(prefix, 0) + 1
                if prefixes[prefix] != 1:
                    prefix = "%s-%s" % (prefix, prefixes[prefix])
                formset = FormSet(instance=obj, prefix=prefix)
                formsets.append(formset)

        adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields)
        media = self.media + adminForm.media

        inline_admin_formsets = []
        for inline, formset in zip(self.inline_instances, formsets):
            fieldsets = list(inline.get_fieldsets(request, obj))
            inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets)
            inline_admin_formsets.append(inline_admin_formset)
            media = media + inline_admin_formset.media

        context = {
            'title': _('Change %s') % force_unicode(opts.verbose_name),
            'adminform': adminForm,
            'object_id': object_id,
            'original': obj,
            'is_popup': request.REQUEST.has_key('_popup'),
            'media': mark_safe(media),
            'inline_admin_formsets': inline_admin_formsets,
            'errors': helpers.AdminErrorList(form, formsets),
            'root_path': self.admin_site.root_path,
            'app_label': opts.app_label,
            'buttons' : self._get_extra_buttons(request, object_id),

        }
        context.update(extra_context or {})
        return self.render_change_form(request, context, change=True, obj=obj)
    change_view = transaction.commit_on_success(change_view)

    def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
        opts = self.model._meta
        app_label = opts.app_label
        ordered_objects = opts.get_ordered_objects()
        context.update({
            'add': add,
            'change': change,
            'has_add_permission': self.has_add_permission(request),
            'has_change_permission': self.has_change_permission(request, obj),
            'has_delete_permission': self.has_delete_permission(request, obj),
            'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
            'has_absolute_url': hasattr(self.model, 'get_absolute_url'),
            'ordered_objects': ordered_objects,
            'form_url': mark_safe(form_url),
            'opts': opts,
            'content_type_id': ContentType.objects.get_for_model(self.model).id,
            'save_as': self.save_as,
            'save_on_top': self.save_on_top,
            'root_path': self.admin_site.root_path,
        })
        context_instance = template.RequestContext(request, current_app=self.admin_site.name)
        return render_to_response(self.get_change_form_template(), 
                                  context, context_instance=context_instance)

    
    def _wrap_line_command(self, action, label, template=None, **kwargs):
        def wrapped(obj):
            link = "%s/op/%s" % (obj.pk, action)
            return '<a href="%s">%s</a>' % (link, label)

        wrapped.allow_tags = True
        return wrapped
    
    def changelist_view(self, request, extra_context=None):
        "The 'change list' admin view for this model."
        from django.contrib.admin.views.main import ChangeList, ERROR_FLAG
        
         
                
        opts = self.model._meta
        app_label = opts.app_label
        if not self.has_change_permission(request, None):
            raise PermissionDenied

        # Check actions to see if any are available on this changelist
        actions = self.get_actions(request)

        # Remove action checkboxes if there aren't any actions available.
        list_display = list(self.list_display)
        if not actions:
            try:
                list_display.remove('action_checkbox')
            except ValueError:
                pass

        for id, cmd in enumerate(self.list_commands):
            func, action, label = self.get_action(cmd)
            list_display.append(self._wrap_line_command(id, label))

        try:
            cl = ChangeList(request, self.model, list_display, self.list_display_links, self.list_filter,
                self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_editable, self)
        except IncorrectLookupParameters:
            # Wacky lookup parameters were given, so redirect to the main
            # changelist page, without parameters, and pass an 'invalid=1'
            # parameter via the query string. If wacky parameters were given and
            # the 'invalid=1' parameter was already in the query string, something
            # is screwed up with the database, so display an error page.
            if ERROR_FLAG in request.GET.keys():
                return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
            return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')

        # If the request was POSTed, this might be a bulk action or a bulk edit.
        # Try to look up an action first, but if this isn't an action the POST
        # will fall through to the bulk edit check, below.
        if actions and request.method == 'POST':
            response = self.response_action(request, queryset=cl.get_query_set())
            if response:
                return response

        # If we're allowing changelist editing, we need to construct a formset
        # for the changelist given all the fields to be edited. Then we'll
        # use the formset to validate/process POSTed data.
        formset = cl.formset = None

        # Handle POSTed bulk-edit data.
        if request.method == "POST" and self.list_editable:
            FormSet = self.get_changelist_formset(request)
            formset = cl.formset = FormSet(request.POST, request.FILES, queryset=cl.result_list)
            if formset.is_valid():
                changecount = 0
                for form in formset.forms:
                    if form.has_changed():
                        obj = self.save_form(request, form, change=True)
                        self.save_model(request, obj, form, change=True)
                        form.save_m2m()
                        change_msg = self.construct_change_message(request, form, None)
                        self.log_change(request, obj, change_msg)
                        changecount += 1

                if changecount:
                    if changecount == 1:
                        name = force_unicode(opts.verbose_name)
                    else:
                        name = force_unicode(opts.verbose_name_plural)
                    msg = ungettext("%(count)s %(name)s was changed successfully.",
                                    "%(count)s %(name)s were changed successfully.",
                                    changecount) % {'count': changecount,
                                                    'name': name,
                                                    'obj': force_unicode(obj)}
                    self.message_user(request, msg)

                return HttpResponseRedirect(request.get_full_path())

        # Handle GET -- construct a formset for display.
        elif self.list_editable:
            FormSet = self.get_changelist_formset(request)
            formset = cl.formset = FormSet(queryset=cl.result_list)

        # Build the list of media to be used by the formset.
        if formset:
            media = self.media + formset.media
        else:
            media = self.media

        # Build the action form and populate it with available actions.
        if actions:
            action_form = self.action_form(auto_id=None)
            action_form.fields['action'].choices = self.get_action_choices(request)
        else:
            action_form = None

        context = {
            'title': cl.title,
            'is_popup': cl.is_popup,
            'cl': cl,
            'media': media,
            'has_add_permission': self.has_add_permission(request),
            'root_path': self.admin_site.root_path,
            'app_label': app_label,
            'action_form': action_form,
            'actions_on_top': self.actions_on_top,
            'actions_on_bottom': self.actions_on_bottom,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(request, current_app=self.admin_site.name)
        return render_to_response(self.change_list_template or [
            'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
            'admin/%s/change_list.html' % app_label,
            'admin/change_list.html'
        ], context, context_instance=context_instance)


class OLD:
    def __init__(self, model, admin_site):

        super(XModelAdmin, self).__init__(model, admin_site)

        class OptionEnhClass(self.opts.__class__):
            def get_read_permission(self):
                return 'read_%s' % self.object_name.lower()

            def get_read_field_restriction(self, field_name):
                return 'cant_read_%s' % field_name

            def get_edit_field_restriction(self, field_name):
                return 'cant_edit_%s' % field_name

        self.opts.__class__ = OptionEnhClass

        self.formfield_overrides.update(XFORMFIELD_FOR_DBFIELD_DEFAULTS)

        self.__mode = XModelAdmin._MODE_UNKNOW
        self._cached_form = None
        self._list_display = copy(self.list_display)
        self._services = {} # dict of ajax service: fieldname: AjaxService
        self.fk_service_viewname = None
        if self.verbose_name_plural is None:
            self.verbose_name_plural = self.model._meta.verbose_name_plural

        self.setup_service_for_foreign_key()
        self._update_inlines()

    def index(self):
        info = self.model._meta.app_label, self.model._meta.module_name

        return HttpResponseRedirect( '.' )
            
    def _update_inlines(self):
        for i in self.inlines:
            setattr(i, "read_only", (i in self.read_only_inlines))

    def setup_service_for_foreign_key(self):
        LOGGER.debug(self.model)
        self.fk_service_viewname = '%s_%s_service_fk' % (self.model._meta.app_label, self.model._meta.module_name)
        if isinstance(self.fk_service, AjaxService):
            self._services['fk'] = (self.fk_service_viewname, self.fk_service)

        elif isinstance(self.fk_service, (list, tuple)) and isinstance(self.fk_service[0], (list, tuple)):
            self.fk_service[0].insert(0, 'pk')
            self._services['fk'] = (self.fk_service_viewname,
                                     AjaxService(self.model.objects.all(), self.fk_service[0], self.fk_service[1])
                                     )

        elif bool(self.fk_service):
            ret_fields = ['pk']
            criteria = []
            for f in self.fk_service:
                ret_fields.append(f)
                criteria.append('%s__istartswith' % f)
            self._services['fk'] = (self.fk_service_viewname,
                                     AjaxService(self.model.objects.all(), ret_fields, criteria)
                                     )
    def formfield_for_dbfield(self, db_field, **kwargs):
        LOGGER.debug(db_field.name)

#        if db_field.name in self.read_only_fields:
#            db_field.__class__ = ROField

        ret = super(XModelAdmin, self).formfield_for_dbfield(db_field, **kwargs)

#        if db_field.name in self.read_only_fields:
#            db_field.widget = HiddenValueWidget

        return ret

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        """
        Get a form Field for a ForeignKey.
        """
        LOGGER.debug(db_field)
        ret = super(XModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
        if ret:
            fk_model_admin = self.admin_site._registry.get(db_field.rel.to)
#    
#            # if foreign key is registered in admin and is XModelAdmin
            if fk_model_admin and hasattr(fk_model_admin, 'fk_service'):
                if bool(fk_model_admin.fk_service):
                    view_name = "%s:%s" % (self.admin_site.name, fk_model_admin.fk_service_viewname)
                    print 111111111, ret
                    ret.widget = AjaxLookupInput(view_name)

        return ret

    def _post_process_form(self, request, form, obj):
        LOGGER.debug("read_only:%s mode:%s " % (self.read_only, self.__mode))
        if not self.read_only and (self.__mode == XModelAdmin._MODE_CHANGE):
            LOGGER.debug(" processing ")
            for field_name in self.read_only_fields:
                if field_name in form.base_fields and form.base_fields.get(field_name, None):
                    form.base_fields[field_name].widget = ReadOnlyWidget(obj, field_name)

            for field_name in self.model_link_fields:
                if field_name in form.base_fields:
                    form.base_fields[field_name].widget = ModelLinkWidget(self.admin_site, obj, field_name)

            for field_name in self.disabled:
                if field_name in form.base_fields:
                    form.base_fields[field_name].widget.attrs['disabled'] = 'disabled'

        for field_name in form.base_fields:
            if self.has_edit_field_restriction(request, field_name=field_name):
                form.base_fields[field_name].widget = ReadOnlyWidget(obj, field_name)
            if self.has_read_field_restriction(request, field_name=field_name):
                form.base_fields[field_name].widget = HiddenValueWidget(obj, field_name)
                #form.base_fields[field_name].label = ""

    def _process_inline_admin_formsets(self, inline_admin_formsets):
        self._update_inlines()
        for inline_admin_formset in inline_admin_formsets:
            if inline_admin_formset.opts.read_only:
                self._set_formset_read_only(inline_admin_formset)

    def _set_formset_read_only(self, inline_admin_formset):
        LOGGER.debug(inline_admin_formset)
        inline_admin_formset.formset.forms = inline_admin_formset.formset.initial_forms
        inline_admin_formset.formset.can_delete = False
        for form in inline_admin_formset.formset.forms:
            for fld in form.visible_fields():
                fld.field.widget = LabelWidget()

    def get_form(self, request, obj=None, **kwargs): #IGNORE:W0613
        LOGGER.debug(obj)
        form = super(XModelAdmin, self).get_form(request, obj)

        self._post_process_form(request, form, obj)

        return form

    def get_formsets(self, request, obj=None):
        LOGGER.debug("obj: %s" % obj)

        if request.method == 'POST':
            # we need to remove ro inlines
            for inline in self.inline_instances:
                if  not inline.read_only:
                    yield inline.get_formset(request, obj)
        else:
            for inline in self.inline_instances:
                yield inline.get_formset(request, obj)

    def _get_default_context(self, request, extra_context, object_id=None):
        LOGGER.debug("%s" % extra_context)


        t = request.GET.get('_tab', 0)
        ctx = {'inline_collapseable': False,
                'inline_orderable': False,
                'has_read_permission': self.has_read_permission(request, None),
                'read_only': self.read_only,
                'selected_tab': t,
                'buttons' : self._get_extra_buttons(request, object_id)
                }
        for i in self.inline_instances:
#            if hasattr(i, 'orderable') and i.orderable:
#                ctx['inline_orderable'] = True
            if hasattr(i, 'collapseable') and i.collapseable:
                ctx['inline_collapseable'] = True


            i.initial_collapsed = getattr(i, 'initial_collapsed', False)

        ctx.update(extra_context or {})
        return ctx

    def _process_change_form_context(self, request, context):
        return context

    def _get_object_by_pk(self, object_id):
        model = self.model
        opts = model._meta

        try:
            obj = self.queryset(None).get(pk=unquote(object_id))
        except model.DoesNotExist:
            # Don't raise Http404 just yet, because we haven't checked
            # permissions yet. We don't want an unauthenticated user to be able
            # to determine whether a given object exists.
            obj = None

        return obj

    def add_view(self, request, form_url='', extra_context=None):
        LOGGER.debug("%s" % extra_context)
        self.__mode = XModelAdmin._MODE_ADD
        extra_context = self._get_default_context(request, extra_context)

        return super(XModelAdmin, self).add_view(request, form_url, extra_context)

    def button_view(self, request, object_id, extra_context=None):
        pass

    def action_view(self, request, object_id, extra_context=None):
        pass







    def history_view(self, request, object_id, extra_context=None):
        obj = self._get_object_by_pk(object_id)
        if not self.has_read_permission(request, obj):
            raise PermissionDenied

        return super(XModelAdmin, self).history_view(request, object_id, extra_context)



    def get_change_list_template(self):
        opts = self.model._meta
        app_label = opts.app_label

        return self.change_list_template or [
            'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
            'admin/%s/change_list.html' % app_label,
            'admin/change_list.html' ]



    def changelist_view(self, request, extra_context=None):
        LOGGER.debug("extra_context: %s" % extra_context)
        self.__mode = XModelAdmin._MODE_LIST
        extra_context = self._get_default_context(request, extra_context)
        extra_context['search_help_text'] = self._get_search_help_text()

        self.change_list_template = self.get_change_list_template()


        cmds = copy(self._list_display)
        for id, cmd in enumerate(self.list_commands):
            func, action, label = self.get_action(cmd)
            cmds.append(self._wrap_line_command(id, label))


        self.list_display = cmds

        return super(XModelAdmin, self).changelist_view(request, extra_context)


    def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
        LOGGER.debug("%s %s" % (context, obj))
        context['read_only'] = self.read_only
        change = change and not self.read_only
        if self.read_only and (self.__mode == XModelAdmin._MODE_CHANGE):
            for perm in ('has_delete_permission', 'has_change_permission'):
                context[perm] = False

            obj = context['original']
            for fld in context['adminform'].form.visible_fields():
                fld.field.widget = ReadOnlyWidget(obj, fld.name)


        self._process_change_form_context(request, context)
        self._process_inline_admin_formsets(context['inline_admin_formsets'])

        self.change_form_template = self.get_change_form_template()

        return super(XModelAdmin, self).render_change_form(request, context, add, change, form_url, obj)



    def response_change(self, request, obj):
        LOGGER.debug("%s" % obj)
        opts = obj._meta

        msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
        if request.POST.has_key("_continue"):
            self.message_user(request, msg + ' ' + _("You may edit it again below."))
            if request.REQUEST.has_key('_popup'):
                return HttpResponseRedirect(request.path + "?_popup=1")
            else:
                return HttpResponseRedirect(request.path + "?_tab=" + request.REQUEST.get('selected_tab', "0"))
        return super(XModelAdmin, self).response_change(request, obj)

    def _get_search_help_text(self):
        LOGGER.debug(self.search_help_text)
        if self.search_help_text is None:
            lbls = []
            for ffilter in self.search_fields:
                f1 = ffilter.split("__")
                if len(f1) > 1:
                    i = "-".join(f1[1:])
                    lbl = "%s (%s)" % (self.model._meta.get_field_by_name(f1[0])[0].verbose_name, i)
                else:
                    lbl = self.model._meta.get_field_by_name(f1[0])[0].verbose_name
                lbls.append(unicode(lbl))
            help_text = force_unicode(", ".join(lbls))
        else:
            help_text = self.search_help_text

        return help_text




#

#





    def has_read_permission(self, request, obj=None): #IGNORE:W0613
        """
        Returns True if the given request has permission to change the given
        Django model instance.

        If `obj` is None, this should return True if the given request has
        permission to change *any* object of the given type.
        """
        LOGGER.debug("%s" % obj)
        opts = self.opts
        return request.user.has_perm(opts.app_label + '.' + opts.get_read_permission())

#    def has_change_permission(self, request, obj=None):
#        if self.__mode == XModelAdmin._MODE_LIST:
#            return self.has_view_permission(request)
#        return (not self.read_only) and super(XModelAdmin, self).has_change_permission(request)

    def has_delete_permission(self, request, obj=None):
        LOGGER.debug("%s" % obj)
        return (not self.read_only) and super(XModelAdmin, self).has_delete_permission(request)
#
#    
    def has_edit_field_restriction(self, request=None, obj=None, field_name=None):
        if request.user.is_superuser:
            return False

        opts = self.opts
        perm_name = opts.get_edit_field_restriction(field_name)
        perm_value = request.user.has_perm(opts.app_label + '.' + perm_name)

        LOGGER.debug("%s.%s : %s (%s) " % (obj, field_name, perm_name, perm_value))
        return perm_value

    def has_read_field_restriction(self, request=None, obj=None, field_name=None):
        if request.user.is_superuser:
            return False
        opts = self.opts
        perm_name = opts.get_read_field_restriction(field_name)
        perm_value = request.user.has_perm(opts.app_label + '.' + perm_name)

        LOGGER.debug("%s.%s : %s (%s) " % (obj, field_name, perm_name, perm_value))
        return perm_value

